#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>

#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>

#include <linux/types.h>
#include <errno.h>
#include <libusb-1.0/libusb.h>

#define VENDOR_ID 0x0525
#define PRODUCT_ID 0xa4a0

void show_use_inf(char *name) {
    printf("Usage:\n");
    printf("%s -l : list bConfigurationValue of all configs\n", name);
    printf("%s -s <bConfigurationValue> : select config\n", name);
    printf("%s -ws <string> : write string\n", name);
    printf("%s -rs : read string\n", name);
    printf("%s -w <val1 val2 ....> : write bytes\n", name);
    printf("%s -r : read 64 bytes\n", name);
}

int main(int argc, char **argv)
{	
    libusb_device_handle *devh = NULL;
    libusb_device *dev = NULL;
    struct libusb_device_descriptor dev_desc;
    struct libusb_config_descriptor *config_desc = NULL;
    const struct libusb_interface_descriptor *intf_desc = NULL;
    int ret = 0;
    int t_num = 0;
    int i = 0;
    int ep_in = 0;
    int ep_out = 0;
    int ep_maxlen = 0;
    int interface_num = 0;
    unsigned char buffer[1024];
    int transferred = 0;

    if (argc == 1) {
        show_use_inf(argv[0]);
        return -1;
    }
    
    /* 初始化libusb */
    ret = libusb_init(NULL);
    if (ret < 0) {
        fprintf(stderr, "Error initializing libusb: %s\n", libusb_error_name(ret));
        return -1;
    }

    /* 打开USB设备 */
    devh = libusb_open_device_with_vid_pid(NULL, VENDOR_ID, PRODUCT_ID);
    if (!devh) {
        fprintf(stderr, "Could not find/open USB device.\n");
        return -1;
    }

    /* 通过libusb_device_handle获取libusb_device */
    dev = libusb_get_device(devh);
    if (!dev) {
        fprintf(stderr, "Could not get device.\n");
        goto errno1;
    }

    /* 获取设备描述符 */
    ret = libusb_get_device_descriptor(dev, &dev_desc);
    if (ret < 0) {
        fprintf(stderr, "Error getting device descriptor %s\n", libusb_strerror(ret));
        goto errno1;
    }

    if (!strcmp(argv[1], "-l")) {
        /* 输出设备所有配置的bConfigurationValue */
        /* 打开虚拟的USB设备,不存在默认配置0,需要遍历获取所有的设备配置描述 */
        
        /* 获取总的设备配置描述符个数 */
        for (i = 0; i < 255; i++) {
            /* 获取配置描述符信息 */
            ret = libusb_get_config_descriptor(dev, i, &config_desc);
            if (LIBUSB_SUCCESS != ret) {
                // 判断是否输出了所有的配置描述符号
                if (t_num == dev_desc.bNumConfigurations) {
                    break;
                } else {
                    continue;
                }
            }
            t_num++;
            printf("bConfigurationValue val = %d\n", config_desc->bConfigurationValue);
            
            libusb_free_config_descriptor(config_desc);
        }

        goto done;
    }

    /* 设置配置描述符bConfigurationValue的值 */
    if (!strcmp(argv[1], "-s") && (argc == 3)) {
        i = atoi(argv[2]);

        // 首先调用libusb_release_interface()。请注意，如果您以这种方式操作
        // 必须确保dev的auto_detach_kernel_driver为0，否则在释放接口时内核驱动程序将重新附加。
        // 如果其他应用程序或驱动程序已声明了接口，则无法更改/重置配置
        // 配置值为-1将使设备处于未配置状态。USB规范规定配置值为0会导致这种情况，但实际上存在有配置0的错误设备。
        // configuration 要激活的配置的bConfigurationValue，如果希望将设备置于未配置状态，则为-1
        // 成功返回0
        libusb_set_auto_detach_kernel_driver(devh, 0);
        libusb_detach_kernel_driver(devh, 0);
        ret = libusb_set_configuration(devh, i);
        if (ret) {
            fprintf(stderr, "could not set configuration as %d, err = %d\n", i, ret);
            goto errno1;
        }

        goto done;
    }

    /* 获取当前的激活的配置描述符bConfigurationValue的值 */
    ret = libusb_get_configuration(devh, &i);
    if (ret) {
        fprintf(stderr, "could not get current configuration, err = %d\n", ret);
        goto errno1;
    }

    printf("current bConfigurationValue value = %d\n", i);

    /* 获取当前配置下接口描述符的输出端点和接收端点 */

    /* 获取当前激活的配置描述符 */
    ret = libusb_get_active_config_descriptor(dev, &config_desc);
    if (ret) {
        fprintf(stderr, "could not get active configuration descriptor, err = %d\n", ret);
        goto errno1;
    }

    // 获取接口描述符
    for (int interface = 0; interface < config_desc->bNumInterfaces; interface++) {
        // 获取USB的配置描述符描述符的配置0中的接口0的描述符
        intf_desc = &config_desc->interface[interface].altsetting[0];
        // 获取接口描述符的bInterfaceNumber;
        interface_num = intf_desc->bInterfaceNumber;
        //获取输出端点和接收端点
        for (i = 0; i < intf_desc->bNumEndpoints; i++) {
            // 判断端点描述符的bmAttributes的USB传输类型是BULK类型
            if ((intf_desc->endpoint[i].bmAttributes & LIBUSB_TRANSFER_TYPE_MASK) == LIBUSB_TRANSFER_TYPE_BULK) {
                // 判断节点是输入端点or输出端点
                if (intf_desc->endpoint[i].bEndpointAddress & LIBUSB_ENDPOINT_IN) {
                    ep_in = intf_desc->endpoint[i].bEndpointAddress;
                    ep_maxlen = intf_desc->endpoint[i].wMaxPacketSize;
                } else {
                    ep_out = intf_desc->endpoint[i].bEndpointAddress;
                    ep_maxlen = intf_desc->endpoint[i].wMaxPacketSize;
                }
            }
        }
    }

    // 释放当前的配置描述符
    libusb_free_config_descriptor(config_desc);

    // 输出输出和输入端点最大数据
    printf("ep_in = %d, ep_out = %d, ep_maxlen = %d\n", ep_in, ep_out, ep_maxlen);

    // 使用libusb_claim_interface时会detach原来的驱动
    libusb_set_auto_detach_kernel_driver(devh, 1);  

    // 标记这个interface已经被使用认领了
    ret = libusb_claim_interface(devh, interface_num);
    if (ret) {
        fprintf(stderr, "Error libusb_claim_interface %s\n", libusb_strerror(ret));
        goto errno1;
    }

    /* write string */
    if (!strcmp(argv[1], "-ws") && (argc == 3)) {
        memset(buffer, 0, 1024);
        strncpy((char *)buffer, argv[2], strlen(argv[2]));
        ret = libusb_bulk_transfer(devh, ep_out, buffer, strlen((char *)buffer), &transferred, 5000);
        if (!ret) {
            // 传输成功
        } else if (ret == LIBUSB_ERROR_TIMEOUT) {
            fprintf(stderr, "libusb_bulk_transfer timout\n");
        } else {
            fprintf(stderr, "libusb_interrupt_transfer err : %d\n", ret);
        }
    }

    if (!strcmp(argv[1], "-rs")) {
        memset(buffer, 0, 1024);
        ret = libusb_bulk_transfer(devh, ep_in, buffer, ep_maxlen, &transferred, 5000);
        if (!ret) {
            // 传输成功
            printf("read str = %s\n", buffer);
        } else if (ret == LIBUSB_ERROR_TIMEOUT) {
            fprintf(stderr, "libusb_bulk_transfer timout\n");
        } else {
            fprintf(stderr, "libusb_interrupt_transfer err : %d\n", ret);
        }
    }

    if (!strcmp(argv[1], "-w") && (argc > 3)) {
        memset(buffer, 0, 1024);
        for (i = 2; i < argc; i++) {
            buffer[i-2] = strtol(argv[i], NULL, 16);
        }

        ret = libusb_bulk_transfer(devh, ep_out, buffer, strlen((char *)buffer), &transferred, 5000);
        if (!ret) {
            // 传输成功
        } else if (ret == LIBUSB_ERROR_TIMEOUT) {
            fprintf(stderr, "libusb_bulk_transfer timout\n");
        } else {
            fprintf(stderr, "libusb_interrupt_transfer err : %d\n", ret);
        }
    }

    if (!strcmp(argv[1], "-r")) {
        memset(buffer, 0, 1024);

        ret = libusb_bulk_transfer(devh, ep_in, buffer, ep_maxlen, &transferred, 5000);
        if (!ret) {
            // 传输成功
            printf("Read datas: \n");
            for (i = 0; i < transferred; i++) {
                printf("%02x ", buffer[i]);
                if ((i+1) % 16 == 0)
                    printf("\n");
            }

            printf("\n");
        } else if (ret == LIBUSB_ERROR_TIMEOUT) {
            fprintf(stderr, "libusb_bulk_transfer timout\n");
        } else {
            fprintf(stderr, "libusb_interrupt_transfer err : %d\n", ret);
        }
    }

    libusb_release_interface(devh, interface_num);
    libusb_close(devh);
    libusb_exit(NULL);

done:
    return 0;

errno1:
    libusb_close(devh);
    libusb_exit(NULL);
	return -1;
}

